// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现选择。

package types

import (
	"bytes"
	"fmt"
)

// SelectionKind描述选择器表达式x.f 
// （不包括限定标识符）的类型。
type SelectionKind int

const (
	FieldVal   SelectionKind = iota // x.f是一个结构域选择器
	MethodVal                       // x.f是一个方法选择器
	MethodExpr                      // x.f是一个方法表达式
)

// x.f.
// 
// 
// 存在以下关系：
// 
// Selector Kind Recv Obj Type Index Index Indirect 
// 
// p.x FieldVal T x int{0}true 
// p.m MethodVal*T m func（）{1,0}true 
// T.m MethodExpr T m func（T）{1，0}false 
// 
type Selection struct {
	kind     SelectionKind
	recv     Type   // x 
}

func (s *Selection) Kind() SelectionKind { return s.kind }

// Recv返回x.f中x的类型。
func (s *Selection) Recv() Type { return s.recv }

// Obj返回x.f表示的对象；
// 的*Var为字段选择，所有其他情况下的*Func为。
func (s *Selection) Obj() Object { return s.obj }

// Type返回x.f的类型，该类型可能与f的类型不同。
// 有关详细信息，请参阅选择。
func (s *Selection) Type() Type {
	switch s.kind {
	case MethodVal:
		// x.f的类型是一种方法，它的接收方类型设置为
		// x的类型。
		sig := *s.obj.(*Func).typ.(*Signature)
		recv := *sig.recv
		recv.typ = s.recv
		sig.recv = &recv
		return &sig

	case MethodExpr:
		// x.f的类型是一个函数（不带接收方）
		// ，另外一个与x类型相同的第一个参数。
		// TODO（gri）类似的代码已经在调用中。加油！
		// TODO（gri）急切地计算这个以避免分配。
		sig := *s.obj.(*Func).typ.(*Signature)
		arg0 := *sig.recv
		sig.recv = nil
		arg0.typ = s.recv
		var params []*Var
		if sig.params != nil {
			params = sig.params.vars
		}
		sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
		return &sig
	}

	// 在所有其他情况下，x.f的类型是x的类型。
	return s.obj.Type()
}

// 索引描述x.f中从x到f的路径。
// 最后一个索引项是声明f的类型的字段或方法索引；
// other:
// 
// 1）命名类型的声明方法列表；或
// 2）接口类型的方法列表；或
// 3）结构类型的字段列表。
// 
// 前面的索引项是嵌入字段的索引，隐式地
// 从嵌入深度0开始，遍历以从（类型）x到f。
func (s *Selection) Index() []int { return s.index }

// 间接报告在x.f中从
// x到f是否需要任何间接指针。
func (s *Selection) Indirect() bool { return s.indirect }

func (s *Selection) String() string { return SelectionString(s, nil) }

// SelectionString返回s的字符串形式。
// 限定符控制
// 包级对象的打印，可能为零。
// 
// /示例：
// “字段（T）f int”
// “方法（T）f（X）Y”
// “方法（T）f（X）Y”
// 
func SelectionString(s *Selection, qf Qualifier) string {
	var k string
	switch s.kind {
	case FieldVal:
		k = "field "
	case MethodVal:
		k = "method "
	case MethodExpr:
		k = "method expr "
	default:
		unreachable()
	}
	var buf bytes.Buffer
	buf.WriteString(k)
	buf.WriteByte('(')
	WriteType(&buf, s.Recv(), qf)
	fmt.Fprintf(&buf, ") %s", s.obj.Name())
	if T := s.Type(); s.kind == FieldVal {
		buf.WriteByte(' ')
		WriteType(&buf, T, qf)
	} else {
		WriteSignature(&buf, T.(*Signature), qf)
	}
	return buf.String()
}
